<?php

namespace app\api\service;


use app\admin\model\Membercoupon;
use app\admin\model\Orderitem;
use app\admin\model\Product;
use app\admin\model\Stock;
use app\api\controller\Freight;
use app\api\model\OrderProduct;
use app\api\model\Order as OrderModel;
use app\common\BaseHelper;
use app\lib\exception\OrderException;
use app\lib\exception\ParameterException;
use app\lib\redis\Redis;
use think\Db;
use think\Exception;

/**
 * 订单类
 * 订单做了以下简化：
 * 创建订单时会检测库存量，并会预扣除库存量，队列支持
 * 未支付的订单再次支付时可能会出现库存不足的情况
 * 所以，项目采用1次检测
 * 1. 创建订单时检测库存
 */
class Order
{
    protected $oProducts;
    protected $products;
    protected $uid;
    protected $addressId = NULL;
    protected $orderType;
    protected $discount_price = 0;
    protected $pickuppoint_id = 0;
    protected $invoice_id;
    protected $content = '';
    protected $my_collect_id = 0;
    protected $deliver_method = 1;
    protected $first_order_reduce = 0; //首单立减金额

    function __construct()
    {
    }

    /**
     * @param int $uid 用户id
     * @param array $oProducts 订单商品列表
     * @return array 订单商品状态
     * @throws Exception
     */
    public function place($uid, $oProducts,$orderData)
    {
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        $this->uid = $uid;
        $this->orderType = $orderData['orderType'];
        if($this->orderType == 1){
            $this->addressId = $orderData['address'];

        }else{
            $this->pickuppoint_id = $orderData['pickuppoint_id'];
            $this->deliver_method = 2;
        }
        if(!empty($orderData['content'])){
            $this->content = $orderData['content'];
        }
        if(!empty($orderData['discount_price'])){
            $this->discount_price = $orderData['discount_price'];
            $this->my_collect_id = $orderData['my_collect_id'];
        }
        $this->address = $orderData['addressId'] ?? NULL;
        $this->invoice_id = $orderData['invoice_id'] ?? NULL;

        $status = $this->getOrderStatus();
        if (!$status['pass']) {
            $status['order_id'] = -1;
            return $status;
        }
        $status['checkcoupon'] = true;
        //如果用了优惠券
        if($this->my_collect_id){
            //检查优惠券的状态
            if(!$this->checkorupdatecoupon($this->my_collect_id)){
                $status['checkcoupon'] = false;
                return $status;
            }
        }

        $orderSnap = $this->snapOrder();
        $status = self::createOrderByTrans($orderSnap);
        $status['checkcoupon'] = true;
        $status['pass'] = true;
        return $status;
    }

    /**
     * @param string $orderNo 订单号
     * @return array 订单商品状态
     * @throws Exception
     */
    public function checkOrderStock($orderID)
    {

        // 一定要从订单商品表中直接查询
        // 不能从商品表中查询订单商品
        // 这将导致被删除的商品无法查询出订单商品来
        $oProducts = Orderitem::where('order_id', '=', $orderID)
            ->select();
        $this->products = $this->getProductsByOrder($oProducts);
        $this->oProducts = $oProducts;
        $status = $this->getOrderStatus();
        return $status;
    }


    private function getOrderStatus()
    {
        $status = [
            'pass' => true,
            'orderPrice' => 0,
            'pStatusArray' => []
        ];
        foreach ($this->oProducts as $oProduct) {
            $pStatus = $this->getProductStatus($oProduct['product_id'], $oProduct['count'], $this->products);
            if (!$pStatus['haveStock']) {
                $status['pass'] = false;
            }
            if (!$pStatus['havegiftStock']) {
                $status['pass'] = false;
            }
            $status['orderPrice'] += $pStatus['totalPrice'];
            array_push($status['pStatusArray'], $pStatus);
        }
        return $status;
    }

    private function getProductStatus($oPID, $oCount, $products)
    {
        $pIndex = -1;
        $pStatus = [
            'id' => null,
            'haveStock' => false,
            'havegiftStock' => false,
            'count' => 0,
            'name' => '',
            'totalPrice' => 0
        ];

        for ($i = 0; $i < count($products); $i++) {
            if ($oPID == $products[$i]['id']) {
                $pIndex = $i;
            }
        }
        if ($pIndex == -1) {
            // 客户端传递的productid有可能根本不存在
            throw new OrderException(
                [
                    'msg' => 'id为' . $oPID . '的商品不存在，订单创建失败'
                ]);
        } else {
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['name'] = $product['name'];
            $pStatus['count'] = $oCount;
            $pStatus['totalPrice'] = $product['price'] * $oCount;

            if ($product['marketable'] - $oCount >= 0) {
                $pStatus['haveStock'] = true;
            }
            //如果有赠品也要判断赠品的库存
            if(isset($product['gift'])){
                if($product['gift']['marketable'] - $product['gift']['count'] >= 0){
                    $pStatus['havegiftStock'] = true;
                }
            }else{
                $pStatus['havegiftStock'] = true;
            }
        }
        return $pStatus;
    }


    // 根据订单查找真实商品
    private function getProductsByOrder($oProducts)
    {
        $oPIDs = []; //普通商品id
        $SoPIDs = [];  //特价商品id
        $goPIDs = [];  //赠送商品id
        $roPIDs = []; //加价换购商品id
        foreach ($oProducts as $item) {
            if($item['prduct_type'] == 0){
                array_push($oPIDs, $item['product_id']);
            }elseif ($item['prduct_type'] == 1){
                array_push($SoPIDs,$item['product_id']);
            }elseif($item['prduct_type'] == 2){
                array_push($oPIDs, $item['product_id']);
                array_push($goPIDs,$item['gift_product_id']);
            }else{
                array_push($roPIDs,$item['product_id']);
            }
        }

        $products01 = [];
        if($oPIDs){
            // 为了避免循环查询数据库
            $join = [
                ['__STOCK__ b','b.product_id=a.id']   //stock
            ];
            $field = 'a.id,a.name,a.price,a.img_url,a.spec,a.unit,a.type,b.marketable';
            $products01 = db('product')->alias('a')->where('a.id','in',$oPIDs)->join($join)->field($field)->select();
            if($products01){
                foreach ($products01 as &$v){
                    foreach ($oProducts as $value){
                        if(isset($value['gift_product_id']) && $v['id'] == $value['product_id']){
                            $v['gift']['product_id'] = $value['gift_product_id'];
                            $marketable = Product::getStock($value['gift_product_id']);
                            $v['gift']['count'] = $value['gift_count'];
                            $v['gift']['marketable'] = $marketable;
                            break;
                        }
                    }
                }
            }
        }
        $products02 = [];
        if($roPIDs){
            $join = [
                ['__STOCK__ b','b.product_id=a.id'],
                ['__REPURCHASE__ c','c.product_id=a.id']
            ];
            $field = 'a.id,a.name,a.price,a.img_url,a.spec,a.unit,a.type,b.marketable,c.markup';
            $products02 = db('product')->alias('a')->where('a.id','in',$roPIDs)->join($join)->field($field)->select();
        }
        $products03 = [];
        if($SoPIDs){
            $join = [
                ['__STOCK__ b','b.product_id=a.id'],
                ['__FLASH_SALE__ c','c.product_id=a.id']
            ];
            $field = 'a.id,a.name,a.price,a.img_url,a.spec,a.unit,a.type,b.marketable,c.markup';
            $products03 = db('product')->alias('a')->where('a.id','in',$SoPIDs)->join($join)->field($field)->select();
        }
        $products = array_merge($products01,$products02,$products03);
        foreach ($products as &$v){
            foreach ($oProducts as $val){
                if($v['id'] == $val['product_id']){
                    $v['type'] = $val['prduct_type'];
                    break;
                }
            }
        }
        return $products;
    }

    private function createOrderByTrans($snap)
    {
        Db::startTrans();
        try {
            $orderNo = $this->makeOrderNo();
            $order = new \app\admin\model\Order();
            $order->id = BaseHelper::getUUID();
            $Freight = new Freight();
            $order->address_id = $this->addressId;
            if(!empty($order->address_id)){
                $order->freight = $Freight->getFreight($this->oProducts,$order->address_id,$snap['orderPrice'],2);
            }else{
                $order->freight = 0;
            }
            $contOrder = new \app\api\controller\Order();
            $res = $contOrder->getfirst_order_price($this->uid,$snap['orderPrice'],2);
            if($res > 0){
                $this->first_order_reduce = $res * 100;
            }
            $order_total = $snap['orderPrice'] + $order->freight - $this->discount_price - $this->first_order_reduce;
            $order->discount_price = $this->discount_price;
            $order->first_order_reduce = $this->first_order_reduce;
            if($this->my_collect_id){
                $order->my_collect_id = $this->my_collect_id;
            }
            $order->member_id = $this->uid;
            $order->order_no = $orderNo;
            $order->order_price = $snap['orderPrice'];
            $order->total = $order_total;
            $order->total_count = $snap['totalCount'];
            $order->invoice_id = $this->invoice_id;
            $order->deliver_method = $this->deliver_method;
            $order->content = $this->content;
            if($this->orderType == 2){
                $order->pickuppoint_id = $this->pickuppoint_id;
            }
            $order->invalid_time = time() + config('order.invalid_time');
            $order->save();
            $orderID = $order->id;
            $create_time = $order->create_date;
            foreach ($this->oProducts as &$p) {
                $p['id'] = BaseHelper::getUUID();
                $p['order_id'] = $orderID;
                foreach ($snap['pStatus'] as $v){
                    if($p['product_id'] == $v['id']){
                        $p['product_id'] =  $v['id'];
                        $p['product_name'] =  $v['name'];
                        $p['product_img'] = $v['main_img_url'];
                        $p['price'] = $v['price'];
                        $p['count'] = $v['count'];
                        $p['spec'] = $v['spec'];
                        $p['unit'] = $v['unit'];
                        if($v['type'] == 1){
                            $p['is_flashsale'] = 1;
                            $p['flashsale_price'] = $v['markup'];
                        }elseif ($v['type'] == 3){
                            $p['is_repurchase'] = 1;
                            $p['repurchase_price'] = $v['markup'];
                        }
                        break;
                    }
                }
            }
            $orderitem = new Orderitem();
            if($orderitem->saveAll($this->oProducts,false)){
                //预扣除库存 1.增加订单占用库存 2.扣除可销售库存
                $this->updateStock($this->oProducts);
                $this->orderQueue($order);
                if($this->my_collect_id){
                    $this->checkorupdatecoupon($this->my_collect_id,2);
                }
            }
            Db::commit();
            //删除购物车
            $this->deleteshopoingcar($this->oProducts,$this->uid);
            return [
                'order_no' => $orderNo,
                'order_id' => $orderID,
                'create_time' => $create_time
            ];
        } catch (Exception $ex) {
            Db::rollback();
            throw $ex;
        }
    }

    private function checkorupdatecoupon($my_collect_id,$type=1){
        $Membercoupon = Membercoupon::where('id',$my_collect_id)->lock(true)->find();
        if($type == 1){
            if($Membercoupon->status == 1){
                return true;
            }
            return false;
        }else{
            $Membercoupon->status = 2;
            $Membercoupon->save();
        }

    }

    private function orderQueue($order){
        $data = [];
        $data['order_id'] = $order->id;
        $data['invalid_time'] = $order->invalid_time;
        $redis = Redis::getRedisConn();
        $redis->RPush('order_list_queue',json_encode($data));
    }
    //预库存操作
    private function updateStock($oProducts){
        foreach ($oProducts as $v){
            $stock = Stock::where('product_id',$v['product_id'])->find();
            if($stock){
                $stock->setDec('marketable',$v['count']);
                $stock->setInc('order_stock',$v['count']);
            }else{
                throw new ParameterException([
                    'msg' => '仓库系统异常'
                ]);
            }
        }
    }

    // 预检测并生成订单快照
    private function snapOrder()
    {
        // status可以单独定义一个类
        $snap = [
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatus' => [],
            'snapName' => $this->products[0]['name'],
            'snapImg' => $this->products[0]['img_url'],
        ];

        if (count($this->products) > 1) {
            $snap['snapName'] .= '等';
        }

        foreach ($this->products as &$v){
            foreach ($this->oProducts as $value){
                if($v['id'] == $value['product_id']){
                    $v['count'] = $value['count'];
                    break;
                }
            }
        }
        for ($i = 0; $i < count($this->products); $i++) {
            $product = $this->products[$i];
            $pStatus = $this->snapProduct($product, $product['count']);
            if(!isset($pStatus['markup'])){
                $snap['orderPrice'] += $product['price'] * $product['count'];
            }else{
                $snap['orderPrice'] += $pStatus['markup'] * $product['count'];
            }
            $snap['totalCount'] += $product['count'];
            array_push($snap['pStatus'], $pStatus);
        }
        return $snap;
    }

    // 单个商品库存检测
    private function snapProduct($product, $oCount)
    {
        $pStatus = [
            'id' => null,
            'name' => null,
            'main_img_url'=>null,
            'count' => $oCount,
            'totalPrice' => 0,
            'price' => 0,
            'spec' => '',
            'unit' => 1
        ];

        $pStatus['counts'] = $oCount;
        // 以服务器价格为准，生成订单

        //是否有特价商品和加价换购商品
        if(!isset($product['markup'])){
            $pStatus['totalPrice'] = $oCount * $product['price'];
        }else{
            $pStatus['markup'] = $product['markup'];
            $pStatus['totalPrice'] = $oCount * $product['markup'];
        }
        $pStatus['name'] = $product['name'];
        $pStatus['id'] = $product['id'];
        $pStatus['main_img_url'] =$product['img_url'];
        $pStatus['price'] = $product['price'];
        $pStatus['spec'] = $product['spec'];
        $pStatus['unit'] = $product['unit'];
        $pStatus['type'] = $product['type'];
        //判断是否有赠品
        if(isset($product['gift'])){
            $pStatus['gift']['product_id'] = $product['gift']['product_id'];
            $pStatus['gift']['counts'] = $product['gift']['count'];
        }

        return $pStatus;
    }

    private function deleteshopoingcar($Oproduct,$uid){
        $product_ids = [];
        foreach ($Oproduct as $v){
            array_push($product_ids,$v['product_id']);
        }
        db('shopping_cart')->where('product_id','in',$product_ids)->where('member_id',$uid)->delete();
    }


    public static function makeOrderNo()
    {
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $orderSn =
            $yCode[intval(date('Y')) - 2017] . strtoupper(dechex(date('m'))) . date(
                'd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf(
                '%02d', rand(0, 99));
        return $orderSn;
    }
}